package com.example.shiro;

import com.example.model.*;
import com.example.service.*;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * Created by zhengkai on 2017/1/23.
 */
public class MyShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;

    /**
     * 认证信息.(身份验证)
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取用户的输入的账号.
        String username = (String)token.getPrincipal();
        System.out.println(token.getCredentials());

        //通过username从数据库中查找 User对象，如果找到，没找到.
        //实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也有时间间隔机制，2分钟内不会重复执行该方法
        List<Userinfo> userinfos = userService.selectByClum("username",username);
        if(userinfos == null || userinfos.size()==0){
            return null;
        }
        // 获取用户信息
        Userinfo userInfo = userinfos.get(0);

        /*
        * 获取权限信息:这里没有进行实现，
        * 请自行根据UserInfo,Role,Permission进行实现；
        * 获取之后可以在前端for循环显示所有链接;
        */
        //userInfo.setPermissions(userService.findPermissions(user));

        //账号判断;
        //加密方式;
        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配，如果觉得人家的不好可以自定义实现
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo, //用户
                userInfo.getPassword(), //密码
                ByteSource.Util.bytes(userInfo.getUsername()+userInfo.getSalt()),//salt=username+salt
                getName()  //realm name
        );

        return authenticationInfo;
    }

    /**
     * 此方法调用  hasRole,hasPermission的时候才会进行回调.
     *
     * 权限信息.(授权):
     * 1、如果用户正常退出，缓存自动清空；
     * 2、如果用户非正常退出，缓存自动清空；
     * 3、如果我们修改了用户的权限，而用户不退出系统，修改的权限无法立即生效。
     * （需要手动编程进行实现；放在service进行调用）
     * 在权限修改后调用realm中的方法，realm已经由spring管理，所以从spring中获取realm实例，
     * 调用clearCached方法；
     * :Authorization 是授权访问控制，用于对用户进行的操作授权，证明该用户是否允许进行当前操作，如访问某个链接，某个资源文件等。
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Userinfo userInfo  = (Userinfo)principalCollection.getPrimaryPrincipal();

        //实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也是有时间间隔机制，2分钟内不会重复执行该方法
        //     UserInfo userInfo = userInfoService.findByUsername(username)

        // 权限单个添加;
        // 或者按下面这样添加
        // 添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
        // authorizationInfo.addRole("admin");
        // 添加权限
        //  authorizationInfo.addStringPermission("userInfo:query");

        ///在认证成功之后返回.
        //设置角色信息.
        //支持 Set集合,
        //用户的角色对应的所有权限，如果只使用角色定义访问权限，下面的四行可以不要
        // List<Role> roleList=user.getRoleList();
        // for (Role role : roleList) {
        //     info.addStringPermissions(role.getPermissionsName());
        //  }

        // addRole配置角色（虽然配置了，但是并没有实现），通过addStringPermission配置权限
        List<Integer> roleIds = userService.listRoles(userInfo.getId());
        for(Integer roleId : roleIds){
            Sysrole role = roleService.roleDetail(roleId);
            authorizationInfo.addRole(role.getRole());

            List<Integer> permissionIds = roleService.listPermission(roleId);
            for(Integer permissionId : permissionIds){
                Syspermission syspermission =permissionService.permissionDetail(permissionId);
                authorizationInfo.addStringPermission(syspermission.getPermission());
            }
        }

        //设置权限信息.另一种配置方式setStringPermissions()
        //     authorizationInfo.setStringPermissions(getStringPermissions(userInfo.getRoleList()));
        return authorizationInfo;
    }
}
